import java.io.IOException;
import java.util.*;
import java.util.regex.*;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.util.*;

/**
 * This is the first pass at the data. Assuming that the record type is
 * the whole XML file, after determining that we are not at at REDIRECT,
 * we find the title of the current page, scan for links, and emit them
 * backwards.
 *
 * @author Ian Voysey
 */
public class JesusMapper extends MapReduceBase
    implements Mapper<Text, Text, Text, Text> {
    private static final int HEADER = 20;

    public void map(Text key,
		    Text value,
		    OutputCollector<Text, Text> output,
		    Reporter reporter)
	throws IOException{
	Text src = new Text();
	Text dest = new Text();

	// if this page is a redirect, we're not going to bother processing
	// it, so we'll test a sufficently large constant chunk of the top
	Scanner sc = new Scanner(value.toString());
	Pattern redirect_p = Pattern.compile(".*#REDIRECT.*", Pattern.CASE_INSENSITIVE);
	int count = 0;

	while(count < HEADER && sc.hasNext()){
	    if(redirect_p.matcher(sc.nextLine()).matches())
		return;
	    count++;
	}
	sc.close();

	// since the first HEADER lines didn't contain '#REDIRECT', it's
	// pretty likely that this is a legit page. we walk the text looking
	// for links, trying to parse out garbage ones to images &c
	sc = new Scanner(value.toString());
	Pattern title_p = Pattern.compile(".*<title>(.+)</title>.*");
	Pattern link_p  = Pattern.compile(".*\\[\\[([a-zA-Z0-9\\-: ]+).*\\]\\].*");
	boolean found_title = false;

	while(sc.hasNext()){
	    String line = sc.nextLine();
	    Matcher title_m = title_p.matcher(line);
	    if(!found_title && title_m.matches()){
		found_title = true;
		if(isGoodLink(title_m.group(1)))
		    dest.set(title_m.group(1));
		else
		    return;
	    } else {
		Matcher link_m = link_p.matcher(line);
		if(link_m.matches() && isGoodLink(link_m.group(1)) ){
		    src.set(link_m.group(1));
		    output.collect(src,dest);
		}
	    }
	}
    }

    /**
     * This function returns true iff the given String is probably an
     * interesting link. This is a modular way to handle a lot of the junk
     * data inherent in the links of the wiki xml. For example, we're not
     * interested in Category pages, Images, links to other language versions,
     * etc.
     *
     * In general, this is a really hard question to answer, so this is a
     * quick-and-dirty hueristic that was reverse engineered from looking at
     * bogus-ish links in the output. It misses roughly less than 1000 links
     * for mysterious, probably unicode related, reasons.
     *
     * @param link this ought to be the first part of a wiki-link, i.e. [[This
     * Part Here:maybe with Weird things|not this part|nor this part]]
     * @return true iff the link is deemed to be "Interesting"
     */
    private static boolean isGoodLink(String link){
	// other language page links
	Pattern other_lang = Pattern.compile("[a-z]{2}:.*");

	// metapages, like images and cat pages.
	Pattern metapage = Pattern.compile(":?(Special|Category|Image|Template|Portal|Wikipedia|MediaWiki):.*",
					   Pattern.CASE_INSENSITIVE);

	if(other_lang.matcher(link).matches() || metapage.matcher(link).matches())
	   return false;
	return true;
    }
}
